<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>学习 redux 源码整体架构，深入理解 redux 及其中间件原理 | 若川的博客</title>
    <meta name="generator" content="VuePress 1.8.2">
    <link rel="icon" href="/favicon.ico">
    <link rel="mainfest" href="/mainfest.json">
    <meta name="description" content="若川，微信搜索「若川视野」关注我，长期交流学习。写有《学习源码整体架构系列》。包含jquery源码、underscore源码、lodash源码、sentry源码、vuex源码、axios源码、koa源码、redux源码。前端路上，PPT爱好者，所知甚少，唯善学。">
    
    <link rel="preload" href="/assets/css/0.styles.0ad39d54.css" as="style"><link rel="preload" href="/assets/js/app.9fbcafa6.js" as="script"><link rel="preload" href="/assets/js/2.33539d56.js" as="script"><link rel="preload" href="/assets/js/12.574e6f26.js" as="script"><link rel="preload" href="/assets/js/23.72249401.js" as="script"><link rel="prefetch" href="/assets/js/10.17a947d6.js"><link rel="prefetch" href="/assets/js/11.599e7eee.js"><link rel="prefetch" href="/assets/js/13.3a93edbc.js"><link rel="prefetch" href="/assets/js/14.c9f20b6b.js"><link rel="prefetch" href="/assets/js/15.c6b03e37.js"><link rel="prefetch" href="/assets/js/16.c445ccb9.js"><link rel="prefetch" href="/assets/js/17.60b94fab.js"><link rel="prefetch" href="/assets/js/18.86de3f95.js"><link rel="prefetch" href="/assets/js/19.6fedd448.js"><link rel="prefetch" href="/assets/js/20.5b11fd5b.js"><link rel="prefetch" href="/assets/js/21.e5faf0b7.js"><link rel="prefetch" href="/assets/js/22.23137eae.js"><link rel="prefetch" href="/assets/js/24.20d474b3.js"><link rel="prefetch" href="/assets/js/25.6dc03c07.js"><link rel="prefetch" href="/assets/js/26.fcf5232b.js"><link rel="prefetch" href="/assets/js/27.6ee352c4.js"><link rel="prefetch" href="/assets/js/28.7534ba1e.js"><link rel="prefetch" href="/assets/js/29.352ed61c.js"><link rel="prefetch" href="/assets/js/3.2f315ac7.js"><link rel="prefetch" href="/assets/js/30.e5dbb079.js"><link rel="prefetch" href="/assets/js/31.b8562982.js"><link rel="prefetch" href="/assets/js/32.f3c8f832.js"><link rel="prefetch" href="/assets/js/33.5ca5188e.js"><link rel="prefetch" href="/assets/js/34.624e3116.js"><link rel="prefetch" href="/assets/js/35.35a86a7c.js"><link rel="prefetch" href="/assets/js/36.21f88fe5.js"><link rel="prefetch" href="/assets/js/37.f5b01c2c.js"><link rel="prefetch" href="/assets/js/38.79ed2093.js"><link rel="prefetch" href="/assets/js/39.687f8425.js"><link rel="prefetch" href="/assets/js/4.9af06e45.js"><link rel="prefetch" href="/assets/js/40.deaa2213.js"><link rel="prefetch" href="/assets/js/41.ede9f8e5.js"><link rel="prefetch" href="/assets/js/42.c48ff846.js"><link rel="prefetch" href="/assets/js/43.012e8b81.js"><link rel="prefetch" href="/assets/js/44.d7040c40.js"><link rel="prefetch" href="/assets/js/45.2805a83b.js"><link rel="prefetch" href="/assets/js/46.95a37284.js"><link rel="prefetch" href="/assets/js/47.d1c213db.js"><link rel="prefetch" href="/assets/js/48.00be6d02.js"><link rel="prefetch" href="/assets/js/49.3d722bd1.js"><link rel="prefetch" href="/assets/js/5.aace9ee0.js"><link rel="prefetch" href="/assets/js/50.d9fa2cde.js"><link rel="prefetch" href="/assets/js/51.e0aaa97a.js"><link rel="prefetch" href="/assets/js/52.a700a7a9.js"><link rel="prefetch" href="/assets/js/53.dbb93ca1.js"><link rel="prefetch" href="/assets/js/6.e0576ee1.js"><link rel="prefetch" href="/assets/js/7.4a4a967d.js"><link rel="prefetch" href="/assets/js/8.b7698a4a.js"><link rel="prefetch" href="/assets/js/9.6930a420.js">
    <link rel="stylesheet" href="/assets/css/0.styles.0ad39d54.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">若川的博客</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="https://image-static.segmentfault.com/355/182/3551821948-5df888aa1dc88_articlex" target="_blank" rel="noopener noreferrer" class="nav-link external">
  公众号：若川视野
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="/" class="nav-link">
  目录
</a></div><div class="nav-item"><a href="/about/" class="nav-link">
  关于我
</a></div><div class="nav-item"><a href="/poetry/2012-2016/" class="nav-link">
  曾经写的&quot;诗词&quot;
</a></div><div class="nav-item"><a href="https://github.com/lxchuan12/blog" target="_blank" rel="noopener noreferrer" class="nav-link external">
  Github
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://juejin.im/user/1415826704971918/posts" target="_blank" rel="noopener noreferrer" class="nav-link external">
  掘金
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.zhihu.com/people/lxchuan12/activities" target="_blank" rel="noopener noreferrer" class="nav-link external">
  知乎
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.yuque.com/lxchuan12/blog" target="_blank" rel="noopener noreferrer" class="nav-link external">
  语雀
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="其他" class="dropdown-title"><span class="title">其他</span> <span class="arrow down"></span></button> <button type="button" aria-label="其他" class="mobile-dropdown-title"><span class="title">其他</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://segmentfault.com/u/lxchuan12/articles" target="_blank" rel="noopener noreferrer" class="nav-link external">
  segmentFault
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://weibo.com/lxchuan12" target="_blank" rel="noopener noreferrer" class="nav-link external">
  微博
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://www.jianshu.com/users/83129d433d72/latest_articles" target="_blank" rel="noopener noreferrer" class="nav-link external">
  简书
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="友链" class="dropdown-title"><span class="title">友链</span> <span class="arrow down"></span></button> <button type="button" aria-label="友链" class="mobile-dropdown-title"><span class="title">友链</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://shanyue.tech" target="_blank" rel="noopener noreferrer" class="nav-link external">
  山月
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://lucifer.ren" target="_blank" rel="noopener noreferrer" class="nav-link external">
  lucifer
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://hungryturbo.com" target="_blank" rel="noopener noreferrer" class="nav-link external">
  童欧巴
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://www.scarsu.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  scarsu
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://mtc.nofwl.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  lencx的博客
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://coder.itclan.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  itclanCoder
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://ruizhengyun.cn" target="_blank" rel="noopener noreferrer" class="nav-link external">
  编程之上
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="https://image-static.segmentfault.com/355/182/3551821948-5df888aa1dc88_articlex" target="_blank" rel="noopener noreferrer" class="nav-link external">
  公众号：若川视野
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="/" class="nav-link">
  目录
</a></div><div class="nav-item"><a href="/about/" class="nav-link">
  关于我
</a></div><div class="nav-item"><a href="/poetry/2012-2016/" class="nav-link">
  曾经写的&quot;诗词&quot;
</a></div><div class="nav-item"><a href="https://github.com/lxchuan12/blog" target="_blank" rel="noopener noreferrer" class="nav-link external">
  Github
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://juejin.im/user/1415826704971918/posts" target="_blank" rel="noopener noreferrer" class="nav-link external">
  掘金
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.zhihu.com/people/lxchuan12/activities" target="_blank" rel="noopener noreferrer" class="nav-link external">
  知乎
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><a href="https://www.yuque.com/lxchuan12/blog" target="_blank" rel="noopener noreferrer" class="nav-link external">
  语雀
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="其他" class="dropdown-title"><span class="title">其他</span> <span class="arrow down"></span></button> <button type="button" aria-label="其他" class="mobile-dropdown-title"><span class="title">其他</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://segmentfault.com/u/lxchuan12/articles" target="_blank" rel="noopener noreferrer" class="nav-link external">
  segmentFault
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://weibo.com/lxchuan12" target="_blank" rel="noopener noreferrer" class="nav-link external">
  微博
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://www.jianshu.com/users/83129d433d72/latest_articles" target="_blank" rel="noopener noreferrer" class="nav-link external">
  简书
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="友链" class="dropdown-title"><span class="title">友链</span> <span class="arrow down"></span></button> <button type="button" aria-label="友链" class="mobile-dropdown-title"><span class="title">友链</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://shanyue.tech" target="_blank" rel="noopener noreferrer" class="nav-link external">
  山月
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="http://lucifer.ren" target="_blank" rel="noopener noreferrer" class="nav-link external">
  lucifer
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://hungryturbo.com" target="_blank" rel="noopener noreferrer" class="nav-link external">
  童欧巴
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://www.scarsu.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  scarsu
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://mtc.nofwl.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  lencx的博客
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://coder.itclan.cn/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  itclanCoder
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://ruizhengyun.cn" target="_blank" rel="noopener noreferrer" class="nav-link external">
  编程之上
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/" aria-current="page" class="sidebar-link">目录</a></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading open"><span>学习源码系列</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/redux/" aria-current="page" class="active sidebar-link">学习 redux 源码整体架构，深入理解 redux 及其中间件原理</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/redux/#_1-前言" class="sidebar-link">1. 前言</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/redux/#_1-1-本文阅读最佳方式" class="sidebar-link">1.1 本文阅读最佳方式</a></li></ul></li><li class="sidebar-sub-header"><a href="/redux/#_2-git-subtree-管理子仓库" class="sidebar-link">2. git subtree 管理子仓库</a></li><li class="sidebar-sub-header"><a href="/redux/#_3-调试-redux-源码准备工作" class="sidebar-link">3. 调试 redux 源码准备工作</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/redux/#_3-1-rollup-生成-sourcemap-便于调试" class="sidebar-link">3.1 rollup 生成 sourcemap 便于调试</a></li></ul></li><li class="sidebar-sub-header"><a href="/redux/#_4-通过调试计数器例子的学习-redux-源码" class="sidebar-link">4. 通过调试计数器例子的学习 redux 源码</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/redux/#_4-1-redux-createsotre" class="sidebar-link">4.1 Redux.createSotre</a></li><li class="sidebar-sub-header"><a href="/redux/#_4-2-store-dispatch-action" class="sidebar-link">4.2 store.dispatch(action)</a></li><li class="sidebar-sub-header"><a href="/redux/#_4-3-store-getstate" class="sidebar-link">4.3 store.getState()</a></li><li class="sidebar-sub-header"><a href="/redux/#_4-4-store-subscribe-listener" class="sidebar-link">4.4 store.subscribe(listener)</a></li></ul></li><li class="sidebar-sub-header"><a href="/redux/#_5-redux-中间件相关源码" class="sidebar-link">5. Redux 中间件相关源码</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/redux/#_5-1-redux-applymiddleware-middlewares" class="sidebar-link">5.1 Redux.applyMiddleware(...middlewares)</a></li><li class="sidebar-sub-header"><a href="/redux/#_5-2-redux-compose-functions" class="sidebar-link">5.2 Redux.compose(...functions)</a></li></ul></li><li class="sidebar-sub-header"><a href="/redux/#_6-redux-combinereducers-reducers" class="sidebar-link">6. Redux.combineReducers(reducers)</a></li><li class="sidebar-sub-header"><a href="/redux/#_7-redux-bindactioncreators-actioncreators-dispatch" class="sidebar-link">7. Redux.bindActionCreators(actionCreators, dispatch)</a></li><li class="sidebar-sub-header"><a href="/redux/#_8-vuex-和-redux-简单对比" class="sidebar-link">8. vuex 和 redux 简单对比</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/redux/#_8-1-源码实现形式" class="sidebar-link">8.1 源码实现形式</a></li><li class="sidebar-sub-header"><a href="/redux/#_8-2-耦合度" class="sidebar-link">8.2 耦合度</a></li><li class="sidebar-sub-header"><a href="/redux/#_8-3-扩展" class="sidebar-link">8.3 扩展</a></li><li class="sidebar-sub-header"><a href="/redux/#_8-4-上手难易度" class="sidebar-link">8.4 上手难易度</a></li></ul></li><li class="sidebar-sub-header"><a href="/redux/#_9-总结" class="sidebar-link">9. 总结</a></li><li class="sidebar-sub-header"><a href="/redux/#推荐阅读" class="sidebar-link">推荐阅读</a></li><li class="sidebar-sub-header"><a href="/redux/#另一个系列" class="sidebar-link">另一个系列</a></li><li class="sidebar-sub-header"><a href="/redux/#关于" class="sidebar-link">关于</a></li><li class="sidebar-sub-header"><a href="/redux/#欢迎加微信交流-微信公众号" class="sidebar-link">欢迎加微信交流 微信公众号</a></li></ul></li><li><a href="/koa/" class="sidebar-link">学习 koa 源码的整体架构，浅析koa洋葱模型原理和co原理</a></li><li><a href="/axios/" class="sidebar-link">学习 axios 源码整体架构，打造属于自己的请求库</a></li><li><a href="/vuex/" class="sidebar-link">学习 vuex 源码整体架构，打造属于自己的状态管理库</a></li><li><a href="/sentry/" class="sidebar-link">学习 sentry 源码整体架构，打造属于自己的前端异常监控SDK</a></li><li><a href="/lodash/" class="sidebar-link">学习 lodash  源码整体架构，打造属于自己的函数式编程类库</a></li><li><a href="/underscore/" class="sidebar-link">学习underscore源码整体架构，打造属于自己的函数式编程类库</a></li><li><a href="/jquery/" class="sidebar-link">学习 jQuery 源码整体架构，打造属于自己的 js 类库</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>面试官问系列</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>历史文章</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>杂文</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>曾经写的&quot;诗词&quot;</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>年度总结</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>关于</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="学习-redux-源码整体架构-深入理解-redux-及其中间件原理"><a href="#学习-redux-源码整体架构-深入理解-redux-及其中间件原理" class="header-anchor">#</a> 学习 redux 源码整体架构，深入理解 redux 及其中间件原理</h1> <h2 id="_1-前言"><a href="#_1-前言" class="header-anchor">#</a> 1. 前言</h2> <blockquote><p>你好，我是<a href="https://lxchuan12.gitee.io" target="_blank" rel="noopener noreferrer">若川<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，微信搜索<a href="https://mp.weixin.qq.com/s/c3hFML3XN9KCUetDOZd-DQ" target="_blank" rel="noopener noreferrer">「若川视野」<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>关注我，专注前端技术分享。欢迎加我微信<code>ruochuan12</code>，加群交流学习。</p></blockquote> <blockquote><p>你好，我是<a href="http://lxchuan12.cn/" target="_blank" rel="noopener noreferrer">若川<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。这是<code>学习源码整体架构系列</code>第八篇。整体架构这词语好像有点大，姑且就算是源码整体结构吧，主要就是学习是代码整体结构，不深究其他不是主线的具体函数的实现。本篇文章学习的是实际仓库的代码。</p></blockquote> <blockquote><p><strong>要是有人说到怎么读源码，正在读文章的你能推荐我的源码系列文章，那真是太好了</strong>。</p></blockquote> <p><code>学习源码整体架构系列</code>文章如下：</p> <blockquote><p>1.<a href="https://juejin.im/post/5d39d2cbf265da1bc23fbd42" target="_blank" rel="noopener noreferrer">学习 jQuery 源码整体架构，打造属于自己的 js 类库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br>
2.<a href="https://juejin.im/post/5d4bf94de51d453bb13b65dc" target="_blank" rel="noopener noreferrer">学习 underscore 源码整体架构，打造属于自己的函数式编程类库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br>
3.<a href="https://juejin.im/post/5d767e1d6fb9a06b032025ea" target="_blank" rel="noopener noreferrer">学习 lodash 源码整体架构，打造属于自己的函数式编程类库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br>
4.<a href="https://juejin.im/post/5dba5a39e51d452a2378348a" target="_blank" rel="noopener noreferrer">学习 sentry 源码整体架构，打造属于自己的前端异常监控SDK<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br>
5.<a href="https://juejin.im/post/5dd4e61a6fb9a05a5c010af0" target="_blank" rel="noopener noreferrer">学习 vuex 源码整体架构，打造属于自己的状态管理库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br>
6.<a href="https://juejin.im/post/5df349b5518825123751ba66" target="_blank" rel="noopener noreferrer">学习 axios 源码整体架构，打造属于自己的请求库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br>
7.<a href="https://juejin.im/post/5e69925cf265da571e262fe6" target="_blank" rel="noopener noreferrer">学习 koa 源码的整体架构，浅析koa洋葱模型原理和co原理<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br>
8.<a href="https://juejin.im/post/5ee63b7d51882542fc6265ad" target="_blank" rel="noopener noreferrer">学习 redux 源码整体架构，深入理解 redux 及其中间件原理<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <p>感兴趣的读者可以点击阅读。<br>
其他源码计划中的有：<a href="https://github.com/lxchuan12/express-analysis" target="_blank" rel="noopener noreferrer"><code>express</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>、<a href="https://github.com/lxchuan12/vue-router-analysis" target="_blank" rel="noopener noreferrer"><code>vue-rotuer</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>、<a href="https://github.com/lxchuan12/react-redux-analysis" target="_blank" rel="noopener noreferrer"><code>react-redux</code><span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 等源码，不知何时能写完（哭泣），欢迎持续关注我（若川）。</p> <p>源码类文章，一般阅读量不高。已经有能力看懂的，自己就看了。不想看，不敢看的就不会去看源码。<br>
所以我的文章，尽量写得让想看源码又不知道怎么看的读者能看懂。</p> <p>阅读本文你将学到：</p> <blockquote><ol><li><code>git subtree</code> 管理子仓库</li> <li>如何学习 <code>redux</code> 源码</li> <li><code>redux</code> 中间件原理</li> <li><code>redux</code> 各个<code>API</code>的实现</li> <li><code>vuex</code> 和 <code>redux</code>  的对比</li> <li>等等</li></ol></blockquote> <h3 id="_1-1-本文阅读最佳方式"><a href="#_1-1-本文阅读最佳方式" class="header-anchor">#</a> 1.1 本文阅读最佳方式</h3> <p>把我的<code>redux</code>源码仓库 <code>git clone https://github.com/lxchuan12/redux-analysis.git</code>克隆下来，顺便<code>star</code>一下<a href="https://github.com/lxchuan12/redux-analysis" target="_blank" rel="noopener noreferrer">我的redux源码学习仓库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>^_^。<strong>跟着文章节奏调试和示例代码调试，用<code>chrome</code>动手调试印象更加深刻</strong>。文章长段代码不用细看，可以调试时再细看。看这类源码文章百遍，可能不如自己多调试几遍。也欢迎加我微信交流<code>ruochuan12</code>。</p> <h2 id="_2-git-subtree-管理子仓库"><a href="#_2-git-subtree-管理子仓库" class="header-anchor">#</a> 2. git subtree 管理子仓库</h2> <p>写了很多源码文章，<code>vuex</code>、<code>axios</code>、<code>koa</code>等都是使用新的仓库克隆一份源码在自己仓库中。
虽然电脑可以拉取最新代码，看到原作者的git信息。但上传到<code>github</code>后。读者却看不到原仓库作者的<code>git</code>信息了。于是我找到了<code>git submodules</code> 方案，但并不是很适合。再后来发现了<code>git subtree</code>。</p> <p>简单说下 <code>npm package</code>和<code>git subtree</code>的区别。
<code>npm package</code>是单向的。<code>git subtree</code>则是双向的。</p> <p>具体可以查看这篇文章<a href="https://segmentfault.com/a/1190000003969060" target="_blank" rel="noopener noreferrer">@德来（原有赞大佬）：用 Git Subtree 在多个 Git 项目间双向同步子项目，附简明使用手册<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <p>学会了<code>git subtree</code>后，我新建了<code>redux-analysis</code>项目后，把<code>redux</code>源码<code>4.x</code>（截止至2020年06月13日，<code>4.x</code>分支最新版本是<code>4.0.5</code>，<code>master</code>分支是<code>ts</code>，文章中暂不想让一些不熟悉<code>ts</code>的读者看不懂）分支克隆到了我的项目里的一个子项目，得以保留<code>git</code>信息。</p> <p>对应命令则是：</p> <div class="language-bash extra-class"><pre class="language-bash"><code><span class="token function">git</span> subtree <span class="token function">add</span> --prefix<span class="token operator">=</span>redux https://github.com/reduxjs/redux.git <span class="token number">4</span>.x
</code></pre></div><h2 id="_3-调试-redux-源码准备工作"><a href="#_3-调试-redux-源码准备工作" class="header-anchor">#</a> 3. 调试 redux 源码准备工作</h2> <p>之前，我在知乎回答了一个问题<a href="https://www.zhihu.com/question/350289336/answer/910970733" target="_blank" rel="noopener noreferrer">若川：一年内的前端看不懂前端框架源码怎么办？<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>
推荐了一些资料，阅读量还不错，大家有兴趣可以看看。主要有四点：<br></p> <blockquote><p>1.借助调试<br>
2.搜索查阅相关高赞文章<br>
3.把不懂的地方记录下来，查阅相关文档<br>
4.总结<br></p></blockquote> <p>看源码调试很重要，所以我的每篇源码文章都详细描述（也许有人看来是比较啰嗦...）如何调试源码。</p> <blockquote><p><strong>断点调试要领：</strong><br> <strong>赋值语句可以一步按<code>F10</code>跳过，看返回值即可，后续详细再看。</strong><br> <strong>函数执行需要断点按<code>F11</code>跟着看，也可以结合注释和上下文倒推这个函数做了什么。</strong><br> <strong>有些不需要细看的，直接按<code>F8</code>走向下一个断点</strong><br> <strong>刷新重新调试按<code>F5</code></strong><br></p></blockquote> <p>调试源码前，先简单看看 <code>redux</code> 的工作流程，有个大概印象。</p> <p><img src="/assets/img/redux-workflow.131e80ac.png" alt="redux 工作流程"></p> <h3 id="_3-1-rollup-生成-sourcemap-便于调试"><a href="#_3-1-rollup-生成-sourcemap-便于调试" class="header-anchor">#</a> 3.1 rollup 生成 sourcemap 便于调试</h3> <p>修改<code>rollup.config.js</code>文件，<code>output</code>输出的配置生成<code>sourcemap</code>。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// redux/rollup.config.js 有些省略</span>
<span class="token keyword">const</span> sourcemap <span class="token operator">=</span> <span class="token punctuation">{</span>
  sourcemap<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

output<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// ...</span>
    <span class="token operator">...</span>sourcemap<span class="token punctuation">,</span>
<span class="token punctuation">}</span>
</code></pre></div><p>安装依赖</p> <div class="language-bash extra-class"><pre class="language-bash"><code><span class="token function">git</span> clone http://github.com/lxchuan12/redux-analysis.git
<span class="token builtin class-name">cd</span> redux-analysi/redux
<span class="token function">npm</span> i
<span class="token function">npm</span> run build
<span class="token comment"># 编译结束后会生成 sourcemap .map格式的文件到 dist、es、lib 目录下。</span>
</code></pre></div><p>仔细看看<code>redux/examples</code>目录和<code>redux/README</code>。</p> <p>这时我在根路径下，新建文件夹<code>examples</code>，把原生<code>js</code>写的计数器<code>redux/examples/counter-vanilla/index.html</code>，复制到<code>examples/index.html</code>。同时把打包后的包含<code>sourcemap</code>的<code>redux/dist</code>目录，复制到<code>examples/dist</code>目录。</p> <p>修改<code>index.html</code>的<code>script</code>的<code>redux.js</code>文件为<code>dist中的路径</code>。</p> <blockquote><p>为了便于区分和调试后续<code>html</code>文件，我把<code>index.html</code>重命名为<code>index.1.redux.getState.dispatch.html</code>。</p></blockquote> <div class="language-bash extra-class"><pre class="language-bash"><code><span class="token comment"># redux-analysis 根目录</span>
<span class="token comment"># 安装启动服务的npm包</span>
<span class="token function">npm</span> i -g http-server
<span class="token builtin class-name">cd</span> examples
hs -p <span class="token number">5000</span>
</code></pre></div><p>就可以开心的调试啦。可以直接克隆我的项目<code>git clone http://github.com/lxchuan12/redux-analysis.git</code>。本地调试，动手实践，容易消化吸收。</p> <h2 id="_4-通过调试计数器例子的学习-redux-源码"><a href="#_4-通过调试计数器例子的学习-redux-源码" class="header-anchor">#</a> 4. 通过调试计数器例子的学习 redux 源码</h2> <p>接着我们来看<code>examples/index.1.redux.getState.dispatch.html</code>文件。先看<code>html</code>部分。只是写了几个 <code>button</code>，比较简单。</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>
    Clicked: <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>value<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>0<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span> times
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>increment<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>+<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>decrement<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>-<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>incrementIfOdd<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Increment if odd<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>incrementAsync<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Increment async<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p><code>js部分</code>，也比较简单。声明了一个<code>counter</code>函数，传递给<code>Redux.createStore(counter)</code>，得到结果<code>store</code>，而<code>store</code>是个对象。<code>render</code>方法渲染数字到页面。用<code>store.subscribe(render)</code>订阅的<code>render</code>方法。还有<code>store.dispatch({type: 'INCREMENT' })</code>方法，调用<code>store.dispatch</code>时会触发<code>render</code>方法。这样就实现了一个计数器。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">counter</span><span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span> action</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> state <span class="token operator">===</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token number">0</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">switch</span> <span class="token punctuation">(</span>action<span class="token punctuation">.</span>type<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">case</span> <span class="token string">'INCREMENT'</span><span class="token operator">:</span>
        <span class="token keyword">return</span> state <span class="token operator">+</span> <span class="token number">1</span>
        <span class="token keyword">case</span> <span class="token string">'DECREMENT'</span><span class="token operator">:</span>
        <span class="token keyword">return</span> state <span class="token operator">-</span> <span class="token number">1</span>
        <span class="token keyword">default</span><span class="token operator">:</span>
        <span class="token keyword">return</span> state
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">var</span> store <span class="token operator">=</span> Redux<span class="token punctuation">.</span><span class="token function">createStore</span><span class="token punctuation">(</span>counter<span class="token punctuation">)</span>
<span class="token keyword">var</span> valueEl <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'value'</span><span class="token punctuation">)</span>

<span class="token keyword">function</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    valueEl<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> store<span class="token punctuation">.</span><span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span>render<span class="token punctuation">)</span>

document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">'INCREMENT'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token comment">// 省略部分暂时无效代码...</span>
</code></pre></div><p>思考：看了这段代码，你会在哪打断点来调试呢。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 四处可以断点来看</span>
<span class="token comment">// 1.</span>
<span class="token keyword">var</span> store <span class="token operator">=</span> Redux<span class="token punctuation">.</span><span class="token function">createStore</span><span class="token punctuation">(</span>counter<span class="token punctuation">)</span>
<span class="token comment">// 2.</span>
<span class="token keyword">function</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
valueEl<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> store<span class="token punctuation">.</span><span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 3.</span>
store<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span>render<span class="token punctuation">)</span>
<span class="token comment">// 4.</span>
store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">'INCREMENT'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre></div><p><img src="/assets/img/redux-debugger.d32dcdeb.png" alt="redux debugger图"></p> <p>图中的右边<code>Scope</code>，有时需要关注下，会显示闭包、全局环境、当前环境等变量，还可以显示函数等具体代码位置，能帮助自己理解代码。</p> <p>断点调试，按<code>F5</code>刷新页面后，按<code>F8</code>，把鼠标放在<code>Redux</code>和<code>store</code>上。</p> <p>可以看到<code>Redux</code>上有好几个方法。分别是：</p> <ul><li>__DO_NOT_USE__ActionTypes: {INIT: &quot;@@redux/INITu.v.d.u.6.r&quot;, REPLACE: &quot;@@redux/REPLACEg.u.u.7.c&quot;, PROBE_UNKNOWN_ACTION: ƒ}</li> <li>applyMiddleware: ƒ applyMiddleware() 函数是一个增强器，组合多个中间件，最终增强<code>store.dispatch</code>函数，<code>dispatch</code>时，可以串联执行所有中间件。</li> <li>bindActionCreators: ƒ bindActionCreators(actionCreators, dispatch) 生成actions，主要用于其他库，比如<code>react-redux</code>。</li> <li>combineReducers: ƒ combineReducers(reducers) 组合多个<code>reducers</code>，返回一个总的<code>reducer</code>函数。</li> <li>compose: ƒ compose() 组合多个函数，从右到左，比如：compose(f, g, h) 最终得到这个结果 (...args) =&gt; f(g(h(...args))).</li> <li>createStore: ƒ createStore(reducer, preloadedState, enhancer) 生成 <code>store</code> 对象</li></ul> <p>再看<code>store</code>也有几个方法。分别是：</p> <ul><li>dispatch: ƒ dispatch(action)  派发动作，也就是把<code>subscribe</code>收集的函数，依次遍历执行</li> <li>subscribe: ƒ subscribe(listener) 订阅收集函数存在数组中，等待触发<code>dispatch</code>依次执行。返回一个取消订阅的函数，可以取消订阅监听。</li> <li>getState: ƒ getState() 获取存在<code>createStore</code>函数内部闭包的对象。</li> <li>replaceReducer: ƒ replaceReducer(nextReducer) 主要用于<code>redux</code>开发者工具，对比当前和上一次操作的异同。有点类似时间穿梭功能。</li> <li>Symbol(observable): ƒ observable()</li></ul> <p>也就是<a href="https://redux.org.js" target="_blank" rel="noopener noreferrer">官方文档redux.org.js<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>上的 <code>API</code>。</p> <p>暂时不去深究每一个<code>API</code>的实现。重新按<code>F5</code>刷新页面，断点到<code>var store = Redux.createStore(counter)</code>。一直按<code>F11</code>，先走一遍主流程。</p> <h3 id="_4-1-redux-createsotre"><a href="#_4-1-redux-createsotre" class="header-anchor">#</a> 4.1 Redux.createSotre</h3> <p><code>createStore</code> 函数结构是这样的，是不是看起来很简单，最终返回对象<code>store</code>，包含<code>dispatch</code>、<code>subscribe</code>、<code>getState</code>、<code>replaceReducer</code>等方法。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 省略了若干代码</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token parameter">reducer<span class="token punctuation">,</span> preloadedState<span class="token punctuation">,</span> enhancer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 省略参数校验和替换</span>
    <span class="token comment">// 当前的 reducer 函数</span>
    <span class="token keyword">let</span> currentReducer <span class="token operator">=</span> reducer
    <span class="token comment">// 当前state</span>
    <span class="token keyword">let</span> currentState <span class="token operator">=</span> preloadedState
    <span class="token comment">// 当前的监听数组函数</span>
    <span class="token keyword">let</span> currentListeners <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
    <span class="token comment">// 下一个监听数组函数</span>
    <span class="token keyword">let</span> nextListeners <span class="token operator">=</span> currentListeners
    <span class="token comment">// 是否正在dispatch中</span>
    <span class="token keyword">let</span> isDispatching <span class="token operator">=</span> <span class="token boolean">false</span>
    <span class="token keyword">function</span> <span class="token function">ensureCanMutateNextListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>nextListeners <span class="token operator">===</span> currentListeners<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        nextListeners <span class="token operator">=</span> currentListeners<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">function</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> currentState
    <span class="token punctuation">}</span>
    <span class="token keyword">function</span> <span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token parameter">listener</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token keyword">function</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token parameter">action</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token keyword">function</span> <span class="token function">replaceReducer</span><span class="token punctuation">(</span><span class="token parameter">nextReducer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token keyword">function</span> <span class="token function">observable</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token comment">// ActionTypes.INIT @@redux/INITu.v.d.u.6.r</span>
    <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token operator">:</span> ActionTypes<span class="token punctuation">.</span><span class="token constant">INIT</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
        dispatch<span class="token punctuation">,</span>
        subscribe<span class="token punctuation">,</span>
        getState<span class="token punctuation">,</span>
        replaceReducer<span class="token punctuation">,</span>
        <span class="token punctuation">[</span>$$observable<span class="token punctuation">]</span><span class="token operator">:</span> observable
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="_4-2-store-dispatch-action"><a href="#_4-2-store-dispatch-action" class="header-anchor">#</a> 4.2 store.dispatch(action)</h3> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token parameter">action</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 判断action是否是对象，不是则报错</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">isPlainObject</span><span class="token punctuation">(</span>action<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>
        <span class="token string">'Actions must be plain objects. '</span> <span class="token operator">+</span>
          <span class="token string">'Use custom middleware for async actions.'</span>
      <span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 判断action.type 是否存在，没有则报错</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> action<span class="token punctuation">.</span>type <span class="token operator">===</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>
        <span class="token string">'Actions may not have an undefined &quot;type&quot; property. '</span> <span class="token operator">+</span>
          <span class="token string">'Have you misspelled a constant?'</span>
      <span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 不是则报错</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>isDispatching<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Reducers may not dispatch actions.'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">try</span> <span class="token punctuation">{</span>
      isDispatching <span class="token operator">=</span> <span class="token boolean">true</span>
      currentState <span class="token operator">=</span> <span class="token function">currentReducer</span><span class="token punctuation">(</span>currentState<span class="token punctuation">,</span> action<span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
        <span class="token comment">// 调用完后置为 false</span>
      isDispatching <span class="token operator">=</span> <span class="token boolean">false</span>
    <span class="token punctuation">}</span>
    <span class="token comment">//  把 收集的函数拿出来依次调用</span>
    <span class="token keyword">const</span> listeners <span class="token operator">=</span> <span class="token punctuation">(</span>currentListeners <span class="token operator">=</span> nextListeners<span class="token punctuation">)</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> listeners<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> listener <span class="token operator">=</span> listeners<span class="token punctuation">[</span>i<span class="token punctuation">]</span>
      <span class="token function">listener</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 最终返回 action</span>
    <span class="token keyword">return</span> action
  <span class="token punctuation">}</span>
</code></pre></div><div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">var</span> store <span class="token operator">=</span> Redux<span class="token punctuation">.</span><span class="token function">createStore</span><span class="token punctuation">(</span>counter<span class="token punctuation">)</span>
</code></pre></div><p>上文调试完了这句。</p> <p>继续按<code>F11</code>调试。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    valueEl<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> store<span class="token punctuation">.</span><span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre></div><h3 id="_4-3-store-getstate"><a href="#_4-3-store-getstate" class="header-anchor">#</a> 4.3 store.getState()</h3> <p><code>getState</code>函数实现比较简单。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 判断正在dispatch中，则报错</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>isDispatching<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>
        <span class="token string">'You may not call store.getState() while the reducer is executing. '</span> <span class="token operator">+</span>
            <span class="token string">'The reducer has already received the state as an argument. '</span> <span class="token operator">+</span>
            <span class="token string">'Pass it down from the top reducer instead of reading it from the store.'</span>
        <span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 返回当前的state</span>
    <span class="token keyword">return</span> currentState
<span class="token punctuation">}</span>
</code></pre></div><h3 id="_4-4-store-subscribe-listener"><a href="#_4-4-store-subscribe-listener" class="header-anchor">#</a> 4.4 store.subscribe(listener)</h3> <p>订阅监听函数，存放在数组中，<code>store.dispatch(action)</code>时遍历执行。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token parameter">listener</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 订阅参数校验不是函数报错</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> listener <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Expected the listener to be a function.'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 正在dispatch中，报错</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>isDispatching<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>
        <span class="token string">'You may not call store.subscribe() while the reducer is executing. '</span> <span class="token operator">+</span>
          <span class="token string">'If you would like to be notified after the store has been updated, subscribe from a '</span> <span class="token operator">+</span>
          <span class="token string">'component and invoke store.getState() in the callback to access the latest state. '</span> <span class="token operator">+</span>
          <span class="token string">'See https://redux.js.org/api-reference/store#subscribelistener for more details.'</span>
      <span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 订阅为 true</span>
    <span class="token keyword">let</span> isSubscribed <span class="token operator">=</span> <span class="token boolean">true</span>

    <span class="token function">ensureCanMutateNextListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    nextListeners<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>listener<span class="token punctuation">)</span>

    <span class="token comment">// 返回一个取消订阅的函数</span>
    <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token function">unsubscribe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isSubscribed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 正在dispatch中，则报错</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>isDispatching<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>
          <span class="token string">'You may not unsubscribe from a store listener while the reducer is executing. '</span> <span class="token operator">+</span>
            <span class="token string">'See https://redux.js.org/api-reference/store#subscribelistener for more details.'</span>
        <span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 订阅为 false</span>
      isSubscribed <span class="token operator">=</span> <span class="token boolean">false</span>

      <span class="token function">ensureCanMutateNextListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token comment">//   找到当前监听函数</span>
      <span class="token keyword">const</span> index <span class="token operator">=</span> nextListeners<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>listener<span class="token punctuation">)</span>
    <span class="token comment">//   在数组中删除</span>
      nextListeners<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span>index<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span>
      currentListeners <span class="token operator">=</span> <span class="token keyword">null</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
</code></pre></div><p>到这里，我们就调试学习完了<code>Redux.createSotre</code>、<code>store.dispatch</code>、<code>store.getState</code>、<code>store.subscribe</code>的源码。</p> <p>接下来，我们写个中间件例子，来调试中间件相关源码。</p> <h2 id="_5-redux-中间件相关源码"><a href="#_5-redux-中间件相关源码" class="header-anchor">#</a> 5. Redux 中间件相关源码</h2> <p>中间件是重点，面试官也经常问这类问题。</p> <h3 id="_5-1-redux-applymiddleware-middlewares"><a href="#_5-1-redux-applymiddleware-middlewares" class="header-anchor">#</a> 5.1 Redux.applyMiddleware(...middlewares)</h3> <h4 id="_5-1-1-准备-logger-例子调试"><a href="#_5-1-1-准备-logger-例子调试" class="header-anchor">#</a> 5.1.1 准备 logger 例子调试</h4> <p>为了调试<code>Redux.applyMiddleware(...middlewares)</code>，我在<code>examples/js/middlewares.logger.example.js</code>写一个简单的<code>logger</code>例子。分别有三个<code>logger1</code>，<code>logger2</code>，<code>logger3</code>函数。由于都是类似，所以我在这里只展示<code>logger1</code>函数。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// examples/js/middlewares.logger.example.js</span>
<span class="token keyword">function</span> <span class="token function">logger1</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> getState <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token parameter">next</span> <span class="token operator">=&gt;</span> <span class="token parameter">action</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'will dispatch--1--next, action:'</span><span class="token punctuation">,</span> next<span class="token punctuation">,</span> action<span class="token punctuation">)</span>

      <span class="token comment">// Call the next dispatch method in the middleware chain.</span>
      <span class="token keyword">const</span> returnValue <span class="token operator">=</span> <span class="token function">next</span><span class="token punctuation">(</span>action<span class="token punctuation">)</span>

      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'state after dispatch--1'</span><span class="token punctuation">,</span> <span class="token function">getState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

      <span class="token comment">// This will likely be the action itself, unless</span>
      <span class="token comment">// a middleware further in chain changed it.</span>
      <span class="token keyword">return</span> returnValue
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 省略 logger2、logger3</span>
</code></pre></div><p><code>logger</code>中间件函数做的事情也比较简单，返回两层函数，<code>next</code>就是下一个中间件函数，调用返回结果。为了让读者能看懂，我把<code>logger1</code>用箭头函数、<code>logger2</code>则用普通函数。</p> <p><code>写好例子后</code>，我们接着来看怎么调试<code>Redux.applyMiddleware(...middlewares))</code>源码。</p> <div class="language-bash extra-class"><pre class="language-bash"><code><span class="token builtin class-name">cd</span> redux-analysis <span class="token operator">&amp;&amp;</span> hs -p <span class="token number">5000</span>
<span class="token comment"># 上文说过npm i -g http-server</span>
</code></pre></div><p>打开<code>http://localhost:5000/examples/index.2.redux.applyMiddleware.compose.html</code>，按<code>F12</code>打开控制台，</p> <p>先点击加号操作+1，把结果展示出来。
<img src="/assets/img/redux-middlewares.9052a675.png" alt="redux 中间件调试图">
从图中可以看出，<code>next</code>则是下一个函数。先1-2-3，再3-2-1这样的顺序。</p> <p>这种也就是我们常说的中间件，面向切面编程（AOP）。
<img src="/assets/img/middleware.dabded55.png" alt="中间件图解"></p> <p>接下来调试，在以下语句打上断点和一些你觉得重要的地方打上断点。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// examples/index.2.redux.applyMiddleware.compose.html</span>
<span class="token keyword">var</span> store <span class="token operator">=</span> Redux<span class="token punctuation">.</span><span class="token function">createStore</span><span class="token punctuation">(</span>counter<span class="token punctuation">,</span> Redux<span class="token punctuation">.</span><span class="token function">applyMiddleware</span><span class="token punctuation">(</span>logger1<span class="token punctuation">,</span> logger2<span class="token punctuation">,</span>  logger3<span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre></div><h4 id="_5-1-2-redux-applymiddleware-middlewares-源码"><a href="#_5-1-2-redux-applymiddleware-middlewares-源码" class="header-anchor">#</a> 5.1.2 Redux.applyMiddleware(...middlewares) 源码</h4> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// redux/src/applyMiddleware.js</span>
<span class="token comment">/**
 * ...
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">applyMiddleware</span><span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>middlewares</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token parameter">createStore</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span>
    <span class="token keyword">let</span> <span class="token function-variable function">dispatch</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>
        <span class="token string">'Dispatching while constructing your middleware is not allowed. '</span> <span class="token operator">+</span>
          <span class="token string">'Other middleware would not be applied to this dispatch.'</span>
      <span class="token punctuation">)</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">const</span> middlewareAPI <span class="token operator">=</span> <span class="token punctuation">{</span>
      getState<span class="token operator">:</span> store<span class="token punctuation">.</span>getState<span class="token punctuation">,</span>
      <span class="token function-variable function">dispatch</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> chain <span class="token operator">=</span> middlewares<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">middleware</span> <span class="token operator">=&gt;</span> <span class="token function">middleware</span><span class="token punctuation">(</span>middlewareAPI<span class="token punctuation">)</span><span class="token punctuation">)</span>
    dispatch <span class="token operator">=</span> <span class="token function">compose</span><span class="token punctuation">(</span><span class="token operator">...</span>chain<span class="token punctuation">)</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span>dispatch<span class="token punctuation">)</span>

    <span class="token keyword">return</span> <span class="token punctuation">{</span>
      <span class="token operator">...</span>store<span class="token punctuation">,</span>
      dispatch
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// redux/src/createStore.js</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token parameter">reducer<span class="token punctuation">,</span> preloadedState<span class="token punctuation">,</span> enhancer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 省略参数校验</span>
  <span class="token comment">// 如果第二个参数`preloadedState`是函数，并且第三个参数`enhancer`是undefined，把它们互换一下。</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> preloadedState <span class="token operator">===</span> <span class="token string">'function'</span> <span class="token operator">&amp;&amp;</span> <span class="token keyword">typeof</span> enhancer <span class="token operator">===</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    enhancer <span class="token operator">=</span> preloadedState
    preloadedState <span class="token operator">=</span> <span class="token keyword">undefined</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> enhancer <span class="token operator">!==</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> enhancer <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Expected the enhancer to be a function.'</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// enhancer 也就是`Redux.applyMiddleware`返回的函数</span>
    <span class="token comment">// createStore 的 args 则是 `reducer, preloadedState`</span>
    <span class="token comment">/**
     * createStore =&gt; (...args) =&gt; {
            const store = createStore(...args)
            return {
              ...store,
               dispatch,
            }
        }
     ** /
    // 最终返回增强的store对象。
    return enhancer(createStore)(reducer, preloadedState)
  }
  // 省略后续代码
}
</span></code></pre></div><p>把接收的中间件函数<code>logger1</code>, <code>logger2</code>, <code>logger3</code>放入到 了<code>middlewares</code>数组中。<code>Redux.applyMiddleware</code>最后返回两层函数。
把中间件函数都混入了参数<code>getState</code>和<code>dispatch</code>。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// examples/index.2.redux.applyMiddleware.compose.html</span>
<span class="token keyword">var</span> store <span class="token operator">=</span> Redux<span class="token punctuation">.</span><span class="token function">createStore</span><span class="token punctuation">(</span>counter<span class="token punctuation">,</span> Redux<span class="token punctuation">.</span><span class="token function">applyMiddleware</span><span class="token punctuation">(</span>logger1<span class="token punctuation">,</span> logger2<span class="token punctuation">,</span>  logger3<span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre></div><p>最后这句其实是返回一个增强了<code>dispatch</code>的<code>store</code>对象。</p> <p>而增强的<code>dispatch</code>函数，则是用<code>Redux.compose(...functions)</code>进行串联起来执行的。</p> <h3 id="_5-2-redux-compose-functions"><a href="#_5-2-redux-compose-functions" class="header-anchor">#</a> 5.2 Redux.compose(...functions)</h3> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">compose</span><span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>funcs</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>funcs<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token parameter">arg</span> <span class="token operator">=&gt;</span> arg
  <span class="token punctuation">}</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>funcs<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> funcs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">return</span> funcs<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token function">b</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// applyMiddleware.js</span>
dispatch <span class="token operator">=</span> <span class="token function">compose</span><span class="token punctuation">(</span><span class="token operator">...</span>chain<span class="token punctuation">)</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span>dispatch<span class="token punctuation">)</span>
<span class="token comment">// compose</span>
funcs<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token function">b</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre></div><p>这两句可能不是那么好理解，可以断点多调试几次。我把箭头函数转换成普通函数。</p> <div class="language-js extra-class"><pre class="language-js"><code>funcs<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token function">b</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>其实<code>redux</code>源码中注释很清晰了，这个<code>compose</code>函数上方有一堆注释，其中有一句：组合多个函数，从右到左，比如：<code>compose(f, g, h)</code> 最终得到这个结果 <code>(...args) =&gt; f(g(h(...args)))</code>.</p> <h4 id="_5-2-1-compose-函数演化"><a href="#_5-2-1-compose-函数演化" class="header-anchor">#</a> 5.2.1 compose 函数演化</h4> <p>看<code>Redux.compose(...functions)</code>函数源码后，还是不明白，不要急不要慌，吃完鸡蛋还有汤。仔细来看如何演化而来，先来简单看下如下需求。</p> <p>传入一个数值，计算数值乘以10再加上10，再减去2。</p> <p>实现起来很简单。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">calc</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">num</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> num <span class="token operator">*</span> <span class="token number">10</span> <span class="token operator">+</span> <span class="token number">10</span> <span class="token operator">-</span> <span class="token number">2</span><span class="token punctuation">;</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 108</span>
</code></pre></div><p>但这样写有个问题，不好扩展，比如我想乘以<code>10</code>时就打印出结果。
为了便于扩展，我们分开写成三个函数。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">multiply</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
   <span class="token keyword">const</span> result <span class="token operator">=</span> x <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">;</span>
   console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">add</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">y</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> y <span class="token operator">+</span> <span class="token number">10</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">minus</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">z</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> z <span class="token operator">-</span> <span class="token number">2</span><span class="token punctuation">;</span>

<span class="token comment">// 计算结果</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">minus</span><span class="token punctuation">(</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token function">multiply</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 100</span>
<span class="token comment">// 108</span>
<span class="token comment">// 这样我们就把三个函数计算结果出来了。</span>
</code></pre></div><p>再来实现一个相对通用的函数，计算这三个函数的结果。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">compose</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">f<span class="token punctuation">,</span> g<span class="token punctuation">,</span> h</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token function">g</span><span class="token punctuation">(</span><span class="token function">h</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> calc <span class="token operator">=</span> <span class="token function">compose</span><span class="token punctuation">(</span>minus<span class="token punctuation">,</span> add<span class="token punctuation">,</span> multiply<span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">calc</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 100</span>
<span class="token comment">// 108</span>
</code></pre></div><p>这样还是有问题，只支持三个函数。我想支持多个函数。
我们了解到数组的<code>reduce</code>方法就能实现这样的功能。
前一个函数</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 我们常用reduce来计算数值数组的总和</span>
<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">4</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">pre<span class="token punctuation">,</span> item<span class="token punctuation">,</span> index<span class="token punctuation">,</span> arr</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'(pre, item, index, arr)'</span><span class="token punctuation">,</span> pre<span class="token punctuation">,</span> item<span class="token punctuation">,</span> index<span class="token punctuation">,</span> arr<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// (pre, item, index, arr) 1 2 1 (5) [1, 2, 3, 4, 5]</span>
  <span class="token comment">// (pre, item, index, arr) 3 3 2 (5) [1, 2, 3, 4, 5]</span>
  <span class="token comment">// (pre, item, index, arr) 6 4 3 (5) [1, 2, 3, 4, 5]</span>
  <span class="token comment">// (pre, item, index, arr) 10 5 4 (5) [1, 2, 3, 4, 5]</span>
  <span class="token keyword">return</span> pre <span class="token operator">+</span> item<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 15</span>
</code></pre></div><p><code>pre</code> 是上一次返回值，在这里是数值<code>1,3,6,10</code>。在下一个例子中则是匿名函数。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token function">b</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p><code>item</code>是<code>2,3,4,5</code>，在下一个例子中是<code>minus、add、multiply</code>。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">compose</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>funcs</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> funcs<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token function">b</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> calc <span class="token operator">=</span> <span class="token function">compose</span><span class="token punctuation">(</span>minus<span class="token punctuation">,</span> add<span class="token punctuation">,</span> multiply<span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">calc</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 100</span>
<span class="token comment">// 108</span>
</code></pre></div><p>而<code>Redux.compose(...functions)</code>其实就是这样，只不过中间件是返回双层函数罢了。</p> <p>所以返回的是<code>next函数</code>，他们串起来执行了，形成了中间件的洋葱模型。
人们都说一图胜千言。我画了一个相对简单的<code>redux</code>中间件原理图。</p> <p><img src="/assets/img/redux.middleware.drawio.a2ee8f2d.png" alt="中间件原理图"></p> <p>如果还不是很明白，建议按照我给出的例子，多调试。</p> <div class="language-bash extra-class"><pre class="language-bash"><code><span class="token builtin class-name">cd</span> redux-analysis <span class="token operator">&amp;&amp;</span> hs -p <span class="token number">5000</span>
<span class="token comment"># 上文说过npm i -g http-server</span>
</code></pre></div><p>打开<code>http://localhost:5000/examples/index.3.html</code>，按<code>F12</code>打开控制台调试。</p> <h4 id="_5-2-2-前端框架的-compose-函数的实现"><a href="#_5-2-2-前端框架的-compose-函数的实现" class="header-anchor">#</a> 5.2.2 前端框架的 compose 函数的实现</h4> <p><strong>lodash</strong>源码中 <code>compose</code>函数的实现，也是类似于数组的<code>reduce</code>，只不过是内部实现的<code>arrayReduce</code></p> <p><a href="https://juejin.im/post/5d767e1d6fb9a06b032025ea#heading-20" target="_blank" rel="noopener noreferrer">引用自我的文章：学习lodash源码整体架构<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// lodash源码</span>
<span class="token keyword">function</span> <span class="token function">baseWrapperValue</span><span class="token punctuation">(</span><span class="token parameter">value<span class="token punctuation">,</span> actions</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">var</span> result <span class="token operator">=</span> value<span class="token punctuation">;</span>
	<span class="token comment">// 如果是lazyWrapper的实例，则调用LazyWrapper.prototype.value 方法，也就是 lazyValue 方法</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>result <span class="token keyword">instanceof</span> <span class="token class-name">LazyWrapper</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		result <span class="token operator">=</span> result<span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token comment">// 类似 [].reduce()，把上一个函数返回结果作为参数传递给下一个函数</span>
	<span class="token keyword">return</span> <span class="token function">arrayReduce</span><span class="token punctuation">(</span>actions<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">result<span class="token punctuation">,</span> action</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token keyword">return</span> action<span class="token punctuation">.</span><span class="token function">func</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>action<span class="token punctuation">.</span>thisArg<span class="token punctuation">,</span> <span class="token function">arrayPush</span><span class="token punctuation">(</span><span class="token punctuation">[</span>result<span class="token punctuation">]</span><span class="token punctuation">,</span> action<span class="token punctuation">.</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span> result<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p><strong>koa-compose</strong>源码也有<code>compose</code>函数的实现。实现是循环加<code>promise</code>。
由于代码比较长我就省略了，具体看链接<a href="https://juejin.im/post/5e69925cf265da571e262fe6#heading-7" target="_blank" rel="noopener noreferrer">若川：学习 koa 源码的整体架构，浅析koa洋葱模型原理和co原理<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>小节 <code>koa-compose 源码</code>（洋葱模型实现）</p> <h2 id="_6-redux-combinereducers-reducers"><a href="#_6-redux-combinereducers-reducers" class="header-anchor">#</a> 6. Redux.combineReducers(reducers)</h2> <p>打开<code>http://localhost:5000/examples/index.4.html</code>，按<code>F12</code>打开控制台，按照给出的例子，调试接下来的<code>Redux.combineReducers(reducers)</code>和<code>Redux.bindActionCreators(actionCreators, dispatch)</code>具体实现。由于文章已经很长了，这两个函数就不那么详细解释了。</p> <p><code>combineReducers</code>函数简单来说就是合并多个<code>reducer</code>为一个函数<code>combination</code>。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">combineReducers</span><span class="token punctuation">(</span><span class="token parameter">reducers</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> reducerKeys <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>reducers<span class="token punctuation">)</span>
  <span class="token keyword">const</span> finalReducers <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> reducerKeys<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> key <span class="token operator">=</span> reducerKeys<span class="token punctuation">[</span>i<span class="token punctuation">]</span>

    <span class="token comment">// 省略一些开发环境判断的代码...</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> reducers<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      finalReducers<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> reducers<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 经过一些处理后得到最后的finalReducerKeys</span>
  <span class="token keyword">const</span> finalReducerKeys <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>finalReducers<span class="token punctuation">)</span>

  <span class="token comment">// 省略一些开发环境判断的代码...</span>

  <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token function">combination</span><span class="token punctuation">(</span><span class="token parameter">state <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> action</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// ... 省略开发环境的一些判断</span>

   <span class="token comment">// 用 hasChanged变量 记录前后 state 是否已经修改</span>
    <span class="token keyword">let</span> hasChanged <span class="token operator">=</span> <span class="token boolean">false</span>
    <span class="token comment">// 声明对象来存储下一次的state</span>
    <span class="token keyword">const</span> nextState <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token comment">//遍历 finalReducerKeys</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> finalReducerKeys<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> key <span class="token operator">=</span> finalReducerKeys<span class="token punctuation">[</span>i<span class="token punctuation">]</span>
      <span class="token keyword">const</span> reducer <span class="token operator">=</span> finalReducers<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
      <span class="token keyword">const</span> previousStateForKey <span class="token operator">=</span> state<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
      <span class="token comment">// 执行 reducer</span>
      <span class="token keyword">const</span> nextStateForKey <span class="token operator">=</span> <span class="token function">reducer</span><span class="token punctuation">(</span>previousStateForKey<span class="token punctuation">,</span> action<span class="token punctuation">)</span>

      <span class="token comment">// 省略容错代码 ...</span>

      nextState<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> nextStateForKey
      <span class="token comment">// 两次 key 对比 不相等则发生改变</span>
      hasChanged <span class="token operator">=</span> hasChanged <span class="token operator">||</span> nextStateForKey <span class="token operator">!==</span> previousStateForKey
    <span class="token punctuation">}</span>
    <span class="token comment">// 最后的 keys 数组对比 不相等则发生改变</span>
    hasChanged <span class="token operator">=</span>
      hasChanged <span class="token operator">||</span> finalReducerKeys<span class="token punctuation">.</span>length <span class="token operator">!==</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">.</span>length
    <span class="token keyword">return</span> hasChanged <span class="token operator">?</span> nextState <span class="token operator">:</span> state
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="_7-redux-bindactioncreators-actioncreators-dispatch"><a href="#_7-redux-bindactioncreators-actioncreators-dispatch" class="header-anchor">#</a> 7. Redux.bindActionCreators(actionCreators, dispatch)</h2> <p>如果第一个参数是一个函数，那就直接返回一个函数。如果是一个对象，则遍历赋值，最终生成<code>boundActionCreators</code>对象。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">bindActionCreator</span><span class="token punctuation">(</span><span class="token parameter">actionCreator<span class="token punctuation">,</span> dispatch</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token function">actionCreator</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> arguments<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">bindActionCreators</span><span class="token punctuation">(</span><span class="token parameter">actionCreators<span class="token punctuation">,</span> dispatch</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> actionCreators <span class="token operator">===</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">bindActionCreator</span><span class="token punctuation">(</span>actionCreators<span class="token punctuation">,</span> dispatch<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// ... 省略一些容错判断</span>

  <span class="token keyword">const</span> boundActionCreators <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> key <span class="token keyword">in</span> actionCreators<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> actionCreator <span class="token operator">=</span> actionCreators<span class="token punctuation">[</span>key<span class="token punctuation">]</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> actionCreator <span class="token operator">===</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      boundActionCreators<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">bindActionCreator</span><span class="token punctuation">(</span>actionCreator<span class="token punctuation">,</span> dispatch<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> boundActionCreators
<span class="token punctuation">}</span>
</code></pre></div><p><code>redux</code>所提供的的<code>API</code> 除了<code>store.replaceReducer(nextReducer)</code>没分析，其他都分析了。</p> <h2 id="_8-vuex-和-redux-简单对比"><a href="#_8-vuex-和-redux-简单对比" class="header-anchor">#</a> 8. vuex 和 redux 简单对比</h2> <h3 id="_8-1-源码实现形式"><a href="#_8-1-源码实现形式" class="header-anchor">#</a> 8.1 源码实现形式</h3> <p>从源码实现上来看，<code>vuex</code>源码主要使用了构造函数，而<code>redux</code>则是多用函数式编程、闭包。</p> <h3 id="_8-2-耦合度"><a href="#_8-2-耦合度" class="header-anchor">#</a> 8.2 耦合度</h3> <p><code>vuex</code> 与 <code>vue</code> 强耦合，脱离了<code>vue</code>则无法使用。而<code>redux</code>跟<code>react</code>没有关系，所以它可以使用于小程序或者<code>jQuery</code>等。如果需要和<code>react</code>使用，还需要结合<code>react-redux</code>库。</p> <h3 id="_8-3-扩展"><a href="#_8-3-扩展" class="header-anchor">#</a> 8.3 扩展</h3> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// logger 插件，具体实现省略</span>
<span class="token keyword">function</span> <span class="token function">logger</span> <span class="token punctuation">(</span><span class="token parameter">store</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'store'</span><span class="token punctuation">,</span> store<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 作为数组传入</span>
<span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  state<span class="token punctuation">,</span>
  getters<span class="token punctuation">,</span>
  actions<span class="token punctuation">,</span>
  mutations<span class="token punctuation">,</span>
  plugins<span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">!==</span> <span class="token string">'production'</span>
    <span class="token operator">?</span> <span class="token punctuation">[</span>logger<span class="token punctuation">]</span>
    <span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// vuex 源码 插件执行部分</span>
<span class="token keyword">class</span> <span class="token class-name">Store</span><span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token comment">// 把vuex的实例对象 store整个对象传递给插件使用</span>
    plugins<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">plugin</span> <span class="token operator">=&gt;</span> <span class="token function">plugin</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p><code>vuex</code>实现扩展则是使用插件形式，而<code>redux</code>是中间件的形式。<code>redux</code>的中间件则是AOP（面向切面编程），<code>redux</code>中<code>Redux.applyMiddleware()</code>其实也是一个增强函数，所以也可以用户来实现增强器，所以<a href="https://www.redux.org.cn/docs/introduction/Ecosystem.html" target="_blank" rel="noopener noreferrer"><code>redux</code>生态<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>比较繁荣。</p> <h3 id="_8-4-上手难易度"><a href="#_8-4-上手难易度" class="header-anchor">#</a> 8.4 上手难易度</h3> <p>相对来说，<code>vuex</code>上手相对简单，<code>redux</code>相对难一些，<code>redux</code>涉及到一些函数式编程、高阶函数、纯函数等概念。</p> <h2 id="_9-总结"><a href="#_9-总结" class="header-anchor">#</a> 9. 总结</h2> <p>文章主要通过一步步调试的方式循序渐进地讲述<code>redux</code>源码的具体实现。旨在教会读者调试源码，不惧怕源码。</p> <p>面试官经常喜欢考写一个<code>redux</code>中间件，说说<code>redux</code>中间件的原理。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">logger1</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> getState <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token parameter">next</span> <span class="token operator">=&gt;</span> <span class="token parameter">action</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> returnValue <span class="token operator">=</span> <span class="token function">next</span><span class="token punctuation">(</span>action<span class="token punctuation">)</span>
      <span class="token keyword">return</span> returnValue
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">compose</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>funcs</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>funcs<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token parameter">arg</span> <span class="token operator">=&gt;</span> arg
  <span class="token punctuation">}</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>funcs<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> funcs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 箭头函数</span>
  <span class="token comment">// return funcs.reduce((a, b) =&gt; (...args) =&gt; a(b(...args)))</span>
  <span class="token keyword">return</span> funcs<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token function">b</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> enhancerStore <span class="token operator">=</span> Redux<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>reducer<span class="token punctuation">,</span> Redux<span class="token punctuation">.</span><span class="token function">applyMiddleware</span><span class="token punctuation">(</span>logger1<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
enhancerStore<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span>action<span class="token punctuation">)</span>
</code></pre></div><p>用户触发<code>enhancerStore.dispatch(action)</code>是增强后的，其实就是第一个中间件函数，中间的<code>next</code>是下一个中间件函数，最后<code>next</code>是没有增强的<code>store.dispatch(action)</code>。</p> <p>最后再来看张<code>redux</code>工作流程图 <img src="/assets/img/redux-workflow-gif.1d8ec674.png" alt="工作流程图">是不是就更理解些了呢。</p> <p>如果读者发现有不妥或可改善之处，再或者哪里没写明白的地方，欢迎评论指出。另外觉得写得不错，对你有些许帮助，可以点赞、评论、转发分享，也是对我的一种支持，非常感谢呀。<strong>要是有人说到怎么读源码，正在读文章的你能推荐我的源码系列文章，那真是太好了</strong>。</p> <h2 id="推荐阅读"><a href="#推荐阅读" class="header-anchor">#</a> 推荐阅读</h2> <p><a href="http://huziketang.mangojuice.top/books/react/lesson30" target="_blank" rel="noopener noreferrer">@胡子大哈：动手实现 Redux（一）：优雅地修改共享状态<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，总共6小节，非常推荐，虽然我很早前就看完了《react小书》，现在再看一遍又有收获<br> <a href="https://tech.meituan.com/2017/07/14/redux-design-code.html" target="_blank" rel="noopener noreferrer">美团@莹莹 Redux从设计到源码<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，美团这篇是我基本写完文章后看到的，感觉写得很好，非常推荐<br> <a href="https://www.redux.org.cn/" target="_blank" rel="noopener noreferrer">redux 中文文档<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br> <a href="https://redux.js.org" target="_blank" rel="noopener noreferrer">redux 英文文档<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br> <a href="http://github.com/lxchuan12/redux-analysis.git" target="_blank" rel="noopener noreferrer">若川的学习redux源码仓库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <h2 id="另一个系列"><a href="#另一个系列" class="header-anchor">#</a> 另一个系列</h2> <p><a href="https://juejin.im/post/5c433e216fb9a049c15f841b" target="_blank" rel="noopener noreferrer">面试官问：JS的继承<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br> <a href="https://juejin.im/post/5c0c87b35188252e8966c78a" target="_blank" rel="noopener noreferrer">面试官问：JS的this指向<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br> <a href="https://juejin.im/post/5bf6c79bf265da6142738b29" target="_blank" rel="noopener noreferrer">面试官问：能否模拟实现JS的call和apply方法<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br> <a href="https://juejin.im/post/5bec4183f265da616b1044d7" target="_blank" rel="noopener noreferrer">面试官问：能否模拟实现JS的bind方法<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br> <a href="https://juejin.im/post/5bde7c926fb9a049f66b8b52" target="_blank" rel="noopener noreferrer">面试官问：能否模拟实现JS的new操作符<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a><br></p> <h2 id="关于"><a href="#关于" class="header-anchor">#</a> 关于</h2> <p>作者：常以<strong>若川</strong>为名混迹于江湖。前端路上 | PPT爱好者 | 所知甚少，唯善学。<br> <a href="https://lxchuan12.cn" target="_blank" rel="noopener noreferrer">若川的博客<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，使用<code>vuepress</code>重构了，阅读体验可能更好些<br> <a href="https://juejin.im/user/57974dc55bbb500063f522fd/posts" target="_blank" rel="noopener noreferrer">掘金专栏<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，欢迎关注~<br> <a href="https://segmentfault.com/blog/lxchuan12" target="_blank" rel="noopener noreferrer"><code>segmentfault</code>前端视野专栏<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，欢迎关注~<br> <a href="https://zhuanlan.zhihu.com/lxchuan12" target="_blank" rel="noopener noreferrer">知乎前端视野专栏<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，欢迎关注~<br> <a href="https://www.yuque.com/lxchuan12/blog" target="_blank" rel="noopener noreferrer">语雀前端视野专栏<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，新增语雀专栏，欢迎关注~<br> <a href="https://github.com/lxchuan12/blog" target="_blank" rel="noopener noreferrer">github blog<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，相关源码和资源都放在这里，求个<code>star</code>^_^~</p> <h2 id="欢迎加微信交流-微信公众号"><a href="#欢迎加微信交流-微信公众号" class="header-anchor">#</a> 欢迎加微信交流 微信公众号</h2> <p>可能比较有趣的微信公众号，长按扫码关注（<strong>回复pdf获取前端优质书籍pdf</strong>）。欢迎加我微信<code>ruochuan12</code>（注明来源，基本来者不拒），拉你进【前端视野交流群】，长期交流学习~</p> <p><img src="https://github.com/lxchuan12/blog/raw/master/docs/about/wechat-official-accounts-mini.jpg" alt="若川视野"></p></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">最后更新时间:</span> <span class="time">4/3/2021, 4:27:16 PM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/" class="prev router-link-active">
        目录
      </a></span> <span class="next"><a href="/koa/">
        学习 koa 源码的整体架构，浅析koa洋葱模型原理和co原理
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"><BackToTop></BackToTop><!----></div></div>
    <script src="/assets/js/app.9fbcafa6.js" defer></script><script src="/assets/js/2.33539d56.js" defer></script><script src="/assets/js/12.574e6f26.js" defer></script><script src="/assets/js/23.72249401.js" defer></script>
  </body>
</html>
